/* LC3 simulator file.t
 * Complete the functions!
 * This is a front-end to the code you wrote in lc3.c
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lc3.h"

void cmd_registers(lc3machine* mach);
void cmd_dump(lc3machine* mach, int start, int end);
void cmd_setaddr(lc3machine* mach, int address, short value);
void cmd_setreg(lc3machine* mach, int reg, short value);

char *PROMPT = "(LC3SIM)";
char *quit = "quit";
char *step = "step";
char *cont = "continue";
char *reg = "registers";
char *dump = "dump";
char *seta = "setaddr";
char *setr = "setreg";
char *help = "help";


int main(int argc, char **argv) {
	FILE *prog;
	//lc3machine mach;

	/* We expect only one argument for the program... */
	if (argc != 2) {
		fprintf(stderr, "Usage: %s file.obj\n", argv[0]);
		return 1;
	}
	/* We want to open the file and make sure that it exists. */
	prog = fopen(argv[1], "rb");
	if (!prog) {
		fprintf(stderr, "Could not find file %s\n", argv[1]);
		return 2;
	}

	/* Make a call to lc3_init to initialize your lc3machine */
	/* Make a call to lc3_load to load the program */
	printf("LC-3 simulator and debugger\n");
	printf("Written by Kyle Kelly\n");

	lc3machine mach;
	lc3_init(&mach);
	lc3_load(&mach, prog);
	fclose(prog);
	
	char input[40];
	char previous[40];
	char * token = NULL;
	/* Run this loop until we are told to stop debugging. */
	while (1) {
		printf("%s", PROMPT);
		strcpy(previous, input);
		char *lulz = fgets(input, 40, stdin);
		if (!lulz) {
			printf("Bad things happened!\n");
		}
		token = strtok(input, " \n\0");
		if(!token){
			strcpy(input, previous);
			token = strtok(input, " \n\0");
		}
		/*Checks for quit*/
		if (!strcmp(token, quit)) {
			printf("Quitting! Goodbye\n");
			break;
		}
		/*Checks for help command*/
		else if(!strcmp(token, help)){
			printf("Valid commands: \n");
			printf("step [n]\n");
			printf("quit\n");
			printf("continue\n");
			printf("registers\n");
			printf("dump start [end]\n");
			printf("setaddr start value\n");
			printf("setreg Rn value\n");
			printf("\n[arg] means arg is optional\n");
		}
		/*Checks and parses for step*/
		else if (!strcmp(token, step)) {
			//printf("%s", token);
			char *steps = strtok(NULL, " \n\0");
			if (steps != NULL) {
				int stepsI = atoi(steps);
				if (stepsI <= 0 || stepsI + mach.pc > 65536)
					printf("Error parsing how many steps. Make sure your step count is positive and doesn't overflow memory area.\n");
				else
					lc3_run(&mach, stepsI);
			}
			else
				lc3_step_one(&mach);
		}
		/*Checks for continue*/
		else if (!strcmp(token, cont)) {
			while (!mach.halted)
				lc3_step_one(&mach);
		}
		/*Checks for register dump*/
		else if (!strcmp(token, reg)) {
			cmd_registers(&mach);
		}
		/*Checks and parses for mem  dump*/
		else if (!strcmp(token, dump)) {
			char *startEnd, *endEnd = NULL;
			char *start = strtok(NULL, " \n\0");
			char *end = strtok(NULL, " \n\0");
			int startInt = strtol(start, &startEnd, 0);
			if (start && *startEnd == '\0') {
				int endInt = strtol(end, &endEnd, 0);
				if (end && !*endEnd) {
					cmd_dump(&mach, startInt, endInt);
				}
				else {
					cmd_dump(&mach, startInt, 1);
				}
			}
			else
				printf("Make sure to give a correct hexadecimal value for your start position as the second parameter\n");
		}
		/*Checks for set address*/
		else if(!strcmp(token, seta)){
			char *addr = strtok(NULL, " \n\0");
			char *value = strtok(NULL, " \n\0");
			if(addr && value){
				char *addrEnd, *valueEnd = NULL;
				int addrInt = strtol(addr, &addrEnd, 0);
				int valueInt = strtol(value, &valueEnd, 0);
				if(addrInt>=0 && addrInt<=65536 && !*addrEnd && !*valueEnd && (valueInt>=-32768) && (valueInt<=32767))
					mach.mem[(unsigned short)addrInt] = (short)valueInt;
				else{
					printf("Make sure your address is an unsigned 16bit and values are valid for 16bit words.\n");
				}
			}
			else
				printf("Make sure you gave an address and value for the memory.\n");
			
			
		}
		/*Checks for set register*/
		else if(!strcmp(token, setr)){
			char *reg = strtok(NULL, " \n\0");
			char *value = strtok(NULL, " \n\0");
			if(reg && value){
				if(*reg == 'R'){
					++reg;
					char *regEnd, *valueEnd = NULL;
					int regInt = strtol(reg, &regEnd, 0);
					int valueInt = strtol(value, &valueEnd, 0);
					//printf("Reg: %i, Value %i\n", regInt, valueInt);
					if(regInt>=0 && regInt<8 && !*regEnd && !*valueEnd && -32768<=valueInt && 32767>=valueInt)
						mach.regs[(short)regInt] = (unsigned short)valueInt;
					else
						printf("Make sure your register is within limits(0-7) and args are parsable numbers\n");
				}
				else
					printf("Make sure to format your register as Rn\n");
			}
			else
				printf("Make sure you give a register (0-7) and value for memory that doesn't overflow the 16bit word\n");
		}
		else
			printf("Invalid command. Please type help see valid commands.\n");
	}
	return 0;
}

/* cmd_step and cmd_continue 's functionality are provided in lc3_run
 Therefore to execute the step and coninute commands you can just call lc3_run with the correct parameters*/

/* cmd_registers
 Should print out all of the registers and the PC and CC in both hex and signed decimal.
 */
void cmd_registers(lc3machine* mach) {
	for (int i = 0; i < 8; i++) {
		printf("R%i\t0x%x\t%i\n", i, mach->regs[i], mach->regs[i]);
	}
	printf("PC\t0x%hx\t%hu\n", mach->pc, mach->pc);
	printf("CC\t0x%hx\t%hu\n", mach->cc, mach->cc);
}

/* cmd_dump
 Should print out the contents of memory from start to end
 If end is -1 then just print out memory[start]*/
void cmd_dump(lc3machine* mach, int start, int end) {
	if (end == -1)
		end = 65536;
	for (int i = start; i < end+1; i++) {
		printf("Addr %x: \t0x%x\t%i\n", i, mach->mem[i], mach->mem[i]);
	}
}

/* cmd_setaddr
 Should set a memory address to some value
 */
void cmd_setaddr(lc3machine* mach, int address, short value) {
	mach->mem[address] = value;
}

/* cmd_setreg
 Should set a register to some value passed in
 */
void cmd_setreg(lc3machine* mach, int reg, short value) {
	mach->regs[reg] = value;
}

